package com.onlyxiahui.common.action.description.util.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessControlException;

/**
 * Description <br>
 * Date 2020-04-06 13:57:22<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class OnlyTypeUtils {

	private static boolean setAccessibleEnable = true;
	private static boolean transientClassInited = false;
	private static Class<? extends Annotation> transientClass;

	static void setAccessible(AccessibleObject obj) {
		if (!setAccessibleEnable) {
			return;
		}
		if (obj.isAccessible()) {
			return;
		}
		try {
			obj.setAccessible(true);
		} catch (AccessControlException error) {
			setAccessibleEnable = false;
		}
	}

	@SuppressWarnings("unchecked")
	public static boolean isTransient(Method method) {
		if (method == null) {
			return false;
		}
		if (!transientClassInited) {
			try {
				transientClass = (Class<? extends Annotation>) Class.forName("java.beans.Transient");
			} catch (Exception e) {
				// skip
			} finally {
				transientClassInited = true;
			}
		}
		if (transientClass != null) {
			Annotation annotation = method.getAnnotation(transientClass);
			return annotation != null;
		}
		return false;
	}

	@SuppressWarnings("rawtypes")
	public static Class<?> getClass(Type type) {
		if (type.getClass() == Class.class) {
			return (Class<?>) type;
		}

		if (type instanceof ParameterizedType) {
			return getClass(((ParameterizedType) type).getRawType());
		}

		if (type instanceof TypeVariable) {
			Type boundType = ((TypeVariable<?>) type).getBounds()[0];
			if (boundType instanceof Class) {
				return (Class) boundType;
			}
			return getClass(boundType);
		}

		if (type instanceof WildcardType) {
			Type[] upperBounds = ((WildcardType) type).getUpperBounds();
			if (upperBounds.length == 1) {
				return getClass(upperBounds[0]);
			}
		}

		return Object.class;
	}

	public static boolean isGenericParamType(Type type) {
		if (type instanceof ParameterizedType) {
			return true;
		}
		if (type instanceof Class) {
			Type superType = ((Class<?>) type).getGenericSuperclass();
			return superType != Object.class && isGenericParamType(superType);
		}
		return false;
	}

	public static Type getGenericParamType(Type type) {
		if (type instanceof ParameterizedType) {
			return type;
		}
		if (type instanceof Class) {
			return getGenericParamType(((Class<?>) type).getGenericSuperclass());
		}
		return type;
	}
}
